package dslProgramGen.newNormalObject;

import dslProgramGen.WhoPointToThisObjectCollection;
import utils.Utils;

import java.util.List;
import java.util.Map;

import static dslProgramGen.DslProgramGen.isGenJavaProgram;
import static dslProgramGen.dslStatement.DslStatement.genAssignNullToLocalRootDslStatement;
import static dslProgramGen.util.DslProgramGenUtil.*;

public class NewNormalObj {
    /**
     * 新建普通对象DSL语句等号右边内容
     *
     * @param newObjectType 新建普通对象类型
     * @return 新建普通对象DSL语句等号右边内容
     */
    public static String genNewNormalObjEqualSignRightStatement(String newObjectType) {
        String rightStatement = null;
        if (isGenJavaProgram) {
            // 如果直接输出的是Java程序
            rightStatement = "new " + newObjectType + "();";
        } else {
            // 如果输出的是DSL程序
            rightStatement = "NewInstance(" + newObjectType + ");";
        }
        return rightStatement;
    }

    /**
     * 新建普通对象 赋值给A.field trace转DSL
     * 生成 A.field = new Student(); DSL语句
     * field可能为静态字段也可能为实例字段
     *
     * @param newObjectType                  新建对象类型
     * @param newObjectId                    新建对象id
     * @param firstLineOfNextNextSiblingNode 新建普通对象一般是TextNode的末尾行，与其匹配的trace一般是next next Node，该变量表示next next Node的第一行内容
     * @param isStatic                       字段是否为静态字段
     * @param dslMethodContentList           生成的DSL程序的函数体
     * @param objId2objExpressionsMap        指定对象id在DSL程序中可以怎样表示出来 <objId, expressions>
     * @param dslObjFieldToObjIdMap          给定DSL语句（对象.field 或 对象数组[index]），看看该DSL语句指向哪个对象Id，主要是帮助处理Put与PutA的trace，减少查询时间
     */
    public static void genNewNormalObjAssignToObjField(String newObjectType,
                                                       String newObjectId,
                                                       String firstLineOfNextNextSiblingNode,
                                                       boolean isStatic,
                                                       List<String> dslMethodContentList,
                                                       Map<String, WhoPointToThisObjectCollection> objId2objExpressionsMap,
                                                       Map<String, String> dslObjFieldToObjIdMap) {
        // 修改静态字段例子 Put H1:0x0000000046cdf8bd, C1:Student, IS:true, H2:0x000000000f0c8a99, C2:Course, F:112, T:main, M:java/lang/module/Configuration.<init> (Ljava/util/List;Ljava/lang/module/Resolver;)V
        // 修改实例字段例子 Put H1:0x000000003de64145, C1:java/lang/module/Configuration, IS:false, H2:0x000000001ecd979e, C2:java/util/ImmutableCollections$List12, F:12, T:main, M:java/lang/module/Configuration.<init> (Ljava/util/List;Ljava/lang/module/Resolver;)V
        String leftStatement;
        if (isStatic) {
            // 修改的是静态字段
            // 生成DSL语句，例如 Student.field = new Student();
            // 获得C1的类名称
            String c1ClassName = Utils.RegularExpressionProcess(firstLineOfNextNextSiblingNode, "C1:(.*?),");
            c1ClassName = renameClassNameReplaceSlashWithUnderlineAndRenameObjectClassName(c1ClassName);
            // 获得修改C1的字段名
            String fieldName = Utils.RegularExpressionProcess(firstLineOfNextNextSiblingNode, "F:(.*?),");

            // 生成DSL语句，例如 Student.field = new Student();
            leftStatement = c1ClassName + "." + fieldName;
            String rightStatement = genNewNormalObjEqualSignRightStatement(newObjectType);
            String dslStatement = genRefUpdateDslStatement(leftStatement, rightStatement);
            // 将该dsl语句加入函数体中
            dslMethodContentList.add(dslStatement);
        } else {
            // 修改的是实例字段
            // 生成DSL语句，例如 A.field = new Student();
            // 获得H1的id
            String h1ObjId = Utils.RegularExpressionProcess(firstLineOfNextNextSiblingNode, "H1:(.*?),");
            // 获得H2的id，也就是newObjectId
            String h2ObjId = Utils.RegularExpressionProcess(firstLineOfNextNextSiblingNode, "H2:(.*?),");
            // 获得修改H1的字段名
            String fieldName = Utils.RegularExpressionProcess(firstLineOfNextNextSiblingNode, "F:(.*?),");
            // 获得H1对象的dsl表示
            if (tryToGetDslExpressionOfObj(h1ObjId, objId2objExpressionsMap) == null) {
                return;
            }
            String h1DslExpression = getDslExpressionOfObj(h1ObjId, objId2objExpressionsMap);

            // 生成DSL语句，例如 A.field = new Student();
            leftStatement = h1DslExpression + "." + fieldName;
            String rightStatement = genNewNormalObjEqualSignRightStatement(newObjectType);
            String dslStatement = genRefUpdateDslStatement(leftStatement, rightStatement);
            // 将该dsl语句加入函数体中
            dslMethodContentList.add(dslStatement);
        }

        // 更新两个数据结构 objId2objExpressionsMap 与 dslObjFieldToObjIdMap
        postProcessNewObjAssignToObjFieldOrArrayIndex(leftStatement, newObjectType, newObjectId,
                objId2objExpressionsMap,
                dslObjFieldToObjIdMap);
    }

    /**
     * 新建普通对象 赋值给array[index] trace转DSL
     * 生成 array[index] = new Student(); DSL语句
     *
     * @param newObjectType                  新建对象类型
     * @param newObjectId                    新建对象id
     * @param firstLineOfNextNextSiblingNode 新建普通对象一般是TextNode的末尾行，与其匹配的trace一般是next next Node，该变量表示next next Node的第一行内容
     * @param dslMethodContentList           生成的DSL程序的函数体
     * @param objId2objExpressionsMap        指定对象id在DSL程序中可以怎样表示出来 <objId, expressions>
     * @param dslObjFieldToObjIdMap          给定DSL语句（对象.field 或 对象数组[index]），看看该DSL语句指向哪个对象Id，主要是帮助处理Put与PutA的trace，减少查询时间
     */
    public static void genNewNormalObjAssignToArrayIndex(String newObjectType,
                                                         String newObjectId,
                                                         String firstLineOfNextNextSiblingNode,
                                                         List<String> dslMethodContentList,
                                                         Map<String, WhoPointToThisObjectCollection> objId2objExpressionsMap,
                                                         Map<String, String> dslObjFieldToObjIdMap) {
        // 修改数组index例子 PutA H1:0x0000000046cdf8bd, C1:[[Ljava/lang/invoke/LambdaForm$Name;, H2:0x000000000f0c8a99, C2:[Ljava/lang/invoke/LambdaForm$Name;, O:0, T:main, M:java/lang/invoke/LambdaForm.<clinit> ()V
        // 生成DSL语句，例如 array[index] = new Student();
        // 获得H1的id
        String h1ObjId = Utils.RegularExpressionProcess(firstLineOfNextNextSiblingNode, "H1:(.*?),");
        // 获得修改H1的index
        String index = Utils.RegularExpressionProcess(firstLineOfNextNextSiblingNode, "O:(.*?),");
        // 获得H1对象的dsl表示
        if (tryToGetDslExpressionOfObj(h1ObjId, objId2objExpressionsMap) == null) {
            return;
        }
        String h1DslExpression = getDslExpressionOfObj(h1ObjId, objId2objExpressionsMap);

        // 生成DSL语句，例如 array[index] = new Student();
        String leftStatement = h1DslExpression + "[" + index + "]";
        String rightStatement = genNewNormalObjEqualSignRightStatement(newObjectType);
        String dslStatement = genRefUpdateDslStatement(leftStatement, rightStatement);
        // 将该dsl语句加入函数体中
        dslMethodContentList.add(dslStatement);

        // 更新两个数据结构 objId2objExpressionsMap 与 dslObjFieldToObjIdMap
        postProcessNewObjAssignToObjFieldOrArrayIndex(leftStatement, newObjectType, newObjectId,
                objId2objExpressionsMap,
                dslObjFieldToObjIdMap);

    }

    /**
     * 产生新建普通对象赋值给新定义的局部变量的DSL语句 例如 Student stu = new Student();
     * @param newObjectId 新建对象id
     * @param newObjectType 新建对象类型
     * @param dslMethodContentList 生成的DSL程序的函数体
     * @param objId2objExpressionsMap 指定对象id在DSL程序中可以怎样表示出来 <objId, expressions>
     * @param classTypeAndNumMap local root命名的辅助结构， 对于新建local root例如Student stu，如何命名stu，当前采用 "student"+num的方式表示
     */
    public static void genNewNormalObjAssignToNewLocalRoot(String newObjectId, String newObjectType, List<String> dslMethodContentList,
                                                           Map<String, WhoPointToThisObjectCollection> objId2objExpressionsMap,
                                                           Map<String, Long> classTypeAndNumMap) {
        // 生成DSL语句，例如 Student stu = new Student();
        // 如果要创建local root，先获得local root的序号
        long prNum = getLocalRootNum(newObjectType, classTypeAndNumMap);
        // 组成local root的全名 小写(类型)_prNum
        String prVariableName = newObjectType.toLowerCase() + "_" + prNum;
        String leftStatement = newObjectType + " " + prVariableName;
        String rightStatement = genNewNormalObjEqualSignRightStatement(newObjectType);
        String dslStatement = leftStatement + " = " + rightStatement;
        // 将该dsl语句加入函数体中
        dslMethodContentList.add(dslStatement);

        // 将newObjectId添加到objId2objExpressionsMap
        WhoPointToThisObjectCollection whoPointToThisObjectCollection = new WhoPointToThisObjectCollection(newObjectType, newObjectId);
        whoPointToThisObjectCollection.lrAndPrPointToThisObjectList.add(prVariableName);
        objId2objExpressionsMap.put(newObjectId, whoPointToThisObjectCollection);
    }

    /**
     * 新建普通对象 赋值给local root trace转DSL
     * 生成 Student stu = new Student(); 或 stu = new Student(); DSL语句
     *
     * @param newObjectType                  新建对象类型
     * @param newObjectId                    新建对象id
     * @param firstLineOfNextNextSiblingNode 新建普通对象一般是TextNode的末尾行，与其匹配的trace一般是next next Node，该变量表示next next Node的第一行内容
     * @param dslMethodContentList           生成的DSL程序的函数体
     * @param objId2objExpressionsMap        指定对象id在DSL程序中可以怎样表示出来 <objId, expressions>
     * @param dslObjFieldToObjIdMap          给定DSL语句（对象.field 或 对象数组[index]），看看该DSL语句指向哪个对象Id，主要是帮助处理Put与PutA的trace，减少查询时间
     * @param classTypeAndNumMap             local root命名的辅助结构， 对于新建local root例如Student stu，如何命名stu，当前采用 "student"+num的方式表示
     */
    public static void genNewNormalObjAssignToLocalRoot(String newObjectType,
                                                        String newObjectId,
                                                        String firstLineOfNextNextSiblingNode,
                                                        List<String> dslMethodContentList,
                                                        Map<String, WhoPointToThisObjectCollection> objId2objExpressionsMap,
                                                        Map<String, String> dslObjFieldToObjIdMap,
                                                        Map<String, Long> classTypeAndNumMap) {
        // 新建local root例子 Add LR H:0x000000004edde6e5, C:java/lang/ThreadGroup, T:main, R: NULL, M:java/lang/ThreadGroup.addUnstarted ()V
        // 给local root赋值例子 Add LR H:0x000000000d716361, C:java/lang/Class, T:main, R:0x000000004459eb14, M:java/util/ImmutableCollections$MapN.<init> ([Ljava/lang/Object;)V

        // 首先获得R部分内容
        String contentOfR = Utils.RegularExpressionProcess(firstLineOfNextNextSiblingNode, "R: ?(.*?),");
        if (contentOfR.equals("NULL")) { // 表示新增一个gc root
            genNewNormalObjAssignToNewLocalRoot(newObjectId, newObjectType, dslMethodContentList,
                    objId2objExpressionsMap,
                    classTypeAndNumMap);
        } else { // 表示修改一个gc root
            // replace对象类型可能和Add LR新对象类型不一致
            // 获得replace对象类型
            String replaceObjType = objId2objExpressionsMap.get(contentOfR).objectType;
            if (newObjectType.equals(replaceObjType)) {
                // 如果Add LR新对象类型和replace对象类型一致
                // 生成DSL语句，例如 stu = new Student();
                // R的内容就是之前local root指向的对象id，获得local root的dsl表示
                String leftStatement = objId2objExpressionsMap.get(contentOfR).lrAndPrPointToThisObjectList.get(0);
                String rightStatement = genNewNormalObjEqualSignRightStatement(newObjectType);
                String dslStatement = leftStatement + " = " + rightStatement;
                // 将该dsl语句加入函数体中
                dslMethodContentList.add(dslStatement);

                // 将newObjectId添加到objId2objExpressionsMap
                WhoPointToThisObjectCollection whoPointToThisObjectCollection = new WhoPointToThisObjectCollection(newObjectType, newObjectId);
                whoPointToThisObjectCollection.lrAndPrPointToThisObjectList.add(leftStatement);
                objId2objExpressionsMap.put(newObjectId, whoPointToThisObjectCollection);
                // 从谁指向R的local root列表中移除leftStatement
                objId2objExpressionsMap.get(contentOfR).lrAndPrPointToThisObjectList.remove(0);
            } else {
                // Add LR新对象类型和replace对象类型不一致
                // 对于Add LR的新值，生成例如 Student stu = new Student(); 这样的DSL语句
                genNewNormalObjAssignToNewLocalRoot(newObjectId, newObjectType, dslMethodContentList,
                        objId2objExpressionsMap,
                        classTypeAndNumMap);
                // 对于replace值，生成 stu = null; 这样的DSL语句
                genAssignNullToLocalRootDslStatement(contentOfR, dslMethodContentList,
                        objId2objExpressionsMap);
            }

        }
    }

    /**
     * 新建普通对象 然后Return该新对象 trace转DSL
     * 本来应该生成 Return new Student(); DSL语句，为简化该过程，分为两步
     * 1. 将new Student()赋值给临时gc root 例如 Student tempStu = new Student();
     * 2. 后续处理Return语句的时候 产生 Return tempStu,并且从谁指向新对象的 newForReturnTempLocalRootList列表中删除 tempStu
     * 该函数主要是产生临时gc root赋值语句，即第一步工作
     *
     * @param newObjectType           新建对象类型
     * @param newObjectId             新建对象id
     * @param dslMethodContentList    生成的DSL程序的函数体
     * @param objId2objExpressionsMap 指定对象id在DSL程序中可以怎样表示出来 <objId, expressions>
     * @param classTypeAndNumMap      local root命名的辅助结构， 对于新建local root例如Student stu，如何命名stu，当前采用 "student"+num的方式表示
     */
    public static void genNewNormalObjAssignToTempLocalRootForReturn(String newObjectType,
                                                                     String newObjectId,
                                                                     List<String> dslMethodContentList,
                                                                     Map<String, WhoPointToThisObjectCollection> objId2objExpressionsMap,
                                                                     Map<String, Long> classTypeAndNumMap) {
        // 生成DSL语句，例如 Student tempStu = new Student();
        // 如果要创建local root，先获得local root的序号
        long prNum = getLocalRootNum(newObjectType, classTypeAndNumMap);
        // 组成临时local root的全名 小写(类型)_tempLocalRootForReturn_prNum
        String prVariableName = newObjectType.toLowerCase() + "_tempLocalRootForReturn_" + prNum;
        String leftStatement = newObjectType + " " + prVariableName;
        String rightStatement = genNewNormalObjEqualSignRightStatement(newObjectType);
        String dslStatement = leftStatement + " = " + rightStatement;
        // 将该dsl语句加入函数体中
        dslMethodContentList.add(dslStatement);

        // 将newObjectId添加到objId2objExpressionsMap，只是这里添加到的是newForReturnTempLocalRootList
        WhoPointToThisObjectCollection whoPointToThisObjectCollection = new WhoPointToThisObjectCollection(newObjectType, newObjectId);
        whoPointToThisObjectCollection.newForReturnTempLocalRootList.add(prVariableName);
        objId2objExpressionsMap.put(newObjectId, whoPointToThisObjectCollection);
    }

    /**
     * 新建普通对象 然后将该新对象传给函数 trace转DSL
     * 本来应该生成 f(new Student()); DSL语句，为简化该过程，分为两步
     * 1. 将new Student()赋值给临时gc root 例如 Student tempStu = new Student();
     * 2. 后续处理函数调用语句的时候 产生 f(tempStu),并且从谁指向新对象的 newForMethodParameterTempLocalRootList列表中删除 tempStu，然后将产生tempStu=null语句
     * 该函数主要是产生临时gc root赋值语句，即第一步工作
     *
     * @param newObjectType           新建对象类型
     * @param newObjectId             新建对象id
     * @param dslMethodContentList    生成的DSL程序的函数体
     * @param objId2objExpressionsMap 指定对象id在DSL程序中可以怎样表示出来 <objId, expressions>
     * @param classTypeAndNumMap      local root命名的辅助结构， 对于新建local root例如Student stu，如何命名stu，当前采用 "student"+num的方式表示
     */
    public static void genNewNormalObjAssignToTempLocalRootForMethodParameter(String newObjectType,
                                                                              String newObjectId,
                                                                              List<String> dslMethodContentList,
                                                                              Map<String, WhoPointToThisObjectCollection> objId2objExpressionsMap,
                                                                              Map<String, Long> classTypeAndNumMap) {
        // 生成DSL语句，例如 Student tempStu = new Student();
        // 如果要创建local root，先获得local root的序号
        long prNum = getLocalRootNum(newObjectType, classTypeAndNumMap);
        // 组成临时local root的全名 小写(类型)_tempLocalRootForMethodParameter_prNum
        String prVariableName = newObjectType.toLowerCase() + "_tempLocalRootForMethodParameter_" + prNum;
        String leftStatement = newObjectType + " " + prVariableName;
        String rightStatement = genNewNormalObjEqualSignRightStatement(newObjectType);
        String dslStatement = leftStatement + " = " + rightStatement;
        // 将该dsl语句加入函数体中
        dslMethodContentList.add(dslStatement);

        // 将newObjectId添加到objId2objExpressionsMap，只是这里添加到的是newForMethodParameterTempLocalRootList
        WhoPointToThisObjectCollection whoPointToThisObjectCollection = new WhoPointToThisObjectCollection(newObjectType, newObjectId);
        whoPointToThisObjectCollection.newForMethodParameterTempLocalRootList.add(prVariableName);
        objId2objExpressionsMap.put(newObjectId, whoPointToThisObjectCollection);
    }

    /**
     * 在生成 A.field = new Student(); 或 array[index] = new Student(); DSL语句之后
     * 更新两个数据结构 objId2objExpressionsMap 与 dslObjFieldToObjIdMap
     *
     * @param leftStatement           用DSL语句表示的等号左边内容
     * @param newObjectType           新建对象类型
     * @param newObjectId             新建对象id
     * @param objId2objExpressionsMap 指定对象id在DSL程序中可以怎样表示出来 <objId, expressions>
     * @param dslObjFieldToObjIdMap   给定DSL语句（对象.field 或 对象数组[index]），看看该DSL语句指向哪个对象Id，主要是帮助处理Put与PutA的trace，减少查询时间
     */
    public static void postProcessNewObjAssignToObjFieldOrArrayIndex(String leftStatement, String newObjectType, String newObjectId,
                                                                     Map<String, WhoPointToThisObjectCollection> objId2objExpressionsMap,
                                                                     Map<String, String> dslObjFieldToObjIdMap) {
        // 更新两个数据结构 objId2objExpressionsMap 与 dslObjFieldToObjIdMap
        // step 1. 更新objId2objExpressionsMap，增加谁指向该新对象
        // 之前没有dsl语句指向该新对象
        WhoPointToThisObjectCollection whoPointToThisObjectTemp = new WhoPointToThisObjectCollection(newObjectType, newObjectId);
        whoPointToThisObjectTemp.fieldOfObjExpressionsPointToThisObjectList.add(leftStatement);
        objId2objExpressionsMap.put(newObjectId, whoPointToThisObjectTemp);

        // step 2. 若A.field或array[index]之前指向对象b，修改objId2objExpressionsMap，删除谁指向对象b的DSL语句中 A.field或array[index]
        // A.field或array[index]之前指向对象b的id
        String prePointObjId = dslObjFieldToObjIdMap.get(leftStatement);
        if (prePointObjId != null) {
            // 移除谁指向b的DSL语句中 A.field或array[index]
            objId2objExpressionsMap.get(prePointObjId).fieldOfObjExpressionsPointToThisObjectList.remove(leftStatement);
        }
        // step 3.修改dslObjFieldToObjIdMap，A.field原来指向对象b，现在指向新建对象
        dslObjFieldToObjIdMap.put(leftStatement, newObjectId);
    }

}
